home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
glass
/
glass.lha
/
GLASS
/
tm
/
tmtrans.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-11-06
|
18KB
|
816 lines
/*
Copyright (C) 1990 C van Reewijk, email: dutentb.uucp!reeuwijk
This file is part of GLASS.
GLASS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
GLASS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GLASS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/* File: tmtrans.c
*
* Translate input file into output file given datastructure
* description.
*/
/* Standard UNIX libraries */
#include <stdio.h>
#include <ctype.h>
#include <tmc.h>
/* local definitions */
#include "tmdefs.h"
#include "tmds.h"
#include "tmstring.h"
#include "debug.h"
#include "tmfn.h"
#include "tmexpr.h"
#include "tmerror.h"
#include "tmglobal.h"
#include "tmlibpath.h"
#include "tmtplelm.h"
#include "tmmisc.h"
#include "tmtrans.h"
#include "tmvar.h"
#include "tmvers.h"
/* mutually recursive local functions */
static void dotrans();
/******************************************************
* *
* Utilities *
* *
******************************************************/
/* tags for command table */
#define EOFLINE 0 /* special line: end of file */
#define PLAIN 1
#define EXIT 2
#define COMMENT 4
#define IF 5
#define ELSE 6
#define ENDIF 7
#define SET 8
#define ERROR 9
#define INCLUDE 10
#define WHILE 11
#define ENDWHILE 12
#define FOREACH 13
#define ENDFOREACH 14
#define INSERT 15
#define APPEND 16
#define COPY 17
/******************************************************
* *
* Input of template lines *
* *
******************************************************/
/* A table of LCOMCHAR commands. The table must be terminated by
an entry with empty name.
*/
struct dotcom {
char *dotcomname;
int dotcomtag;
};
struct dotcom dotcomlist[] = {
{ "append", APPEND },
{ "copy", COPY },
{ "else", ELSE },
{ "endforeach", ENDFOREACH },
{ "endif", ENDIF },
{ "endwhile", ENDWHILE },
{ "error", ERROR },
{ "exit", EXIT },
{ "foreach", FOREACH },
{ "if", IF },
{ "include", INCLUDE },
{ "insert", INSERT },
{ "set", SET },
{ "while", WHILE },
{ "", 0 } /* end of table mark */
};
/* Generate error messages for unbalance in the .<command> and
.end<command> pairs.
*/
static void unbalance( lno, isterm, needterm )
int lno;
int isterm;
int needterm;
{
if( isterm == needterm ) return; /* just for safety */
if( needterm != EOFLINE ){
if( isterm != EOFLINE ){
(void) sprintf( errarg, "unbalanced command at %s(%d)", tplfilename, lno );
line_error( UNEXPECTDOT );
}
else {
(void) sprintf( errarg, "unterminated command at %s(%d)", tplfilename, lno );
line_error( UNEXPECTEOF );
}
}
else {
line_error( EXTRATERM );
}
}
/* Given a file 'f' and a pointer to an int 'endcom', read all lines from
file 'f' up to the next unbalanced end command and put them in template
elements. End commands are:
<eof>
.else
.endif
.endwhile
.endforeach
Return the list of template elements, and set *endcom to the end command
that caused termination.
*/
static tplelm readtemplate( f, endcom )
FILE *f;
int *endcom;
{
char *com;
char *p;
tplelm head;
tplelm *link;
tplelm e1;
tplelm e2;
struct dotcom *cp;
tplelm te;
int subendcom;
int firstlno;
unsigned int inbufsz;
register unsigned int bufix;
register char *inbuf;
register int c;
inbufsz = 100; /* reasonable initial value */
inbuf = ckmalloc( inbufsz );
link = &head;
for(;;){
bufix = 0;
for(;;){
if( bufix>=inbufsz ){
inbufsz += inbufsz;
inbuf = ckrealloc( inbuf, inbufsz );
}
c = getc( f );
if( c == EOF || c == '\n' ){
inbuf[bufix] = '\0';
break;
}
if( c != '\r' ){
inbuf[bufix++] = c;
}
}
tpllineno++;
if( feof( f ) ){
*link = tplelmNIL;
*endcom = EOFLINE;
free( inbuf );
return( head );
}
else if( inbuf[0] == LCOMCHAR ){
if( inbuf[1] != LCOMCHAR ){
p = scanword( inbuf+1, &com );
if( com == CHARNIL ) com = new_string( "" );
cp = dotcomlist;
while( strcmp( com, cp->dotcomname ) != 0 ){
if( cp->dotcomname[0] == '\0' ){
(void) strcpy( errarg, com );
line_error( BADDOTCOM );
exit( 1 );
}
cp++;
}
fre_string( com );
switch( cp->dotcomtag ){
case ENDFOREACH:
case ELSE:
case ENDIF:
case ENDWHILE:
case EOFLINE:
*link = tplelmNIL;
*endcom = cp->dotcomtag;
free( inbuf );
return( head );
case IF:
firstlno = tpllineno;
e1 = readtemplate( f, &subendcom );
if( subendcom == ELSE ){
e2 = readtemplate( f, &subendcom );
}
else {
e2 = tplelmNIL;
}
if( subendcom != ENDIF ){
unbalance( firstlno, subendcom, ENDIF );
}
te = new_If( firstlno, new_string( p ), e1, e2 );
break;
case FOREACH:
firstlno = tpllineno;
e1 = readtemplate( f, &subendcom );
if( subendcom != ENDFOREACH ){
unbalance( firstlno, subendcom, ENDFOREACH );
}
te = new_Foreach( firstlno, new_string( p ), e1 );
break;
case WHILE:
firstlno = tpllineno;
e1 = readtemplate( f, &subendcom );
if( subendcom != ENDWHILE ){
unbalance( firstlno, subendcom, ENDWHILE );
}
te = new_While( firstlno, new_string( p ), e1 );
break;
case INSERT:
te = new_Insert( tpllineno, new_string( p ) );
break;
case INCLUDE:
te = new_Include( tpllineno, new_string( p ) );
break;
case COPY:
te = new_Copy( tpllineno, new_string( p ) );
break;
case SET:
te = new_Set( tpllineno, new_string( p ) );
break;
case APPEND:
te = new_Append( tpllineno, new_string( p ) );
break;
case EXIT:
te = new_Exit( tpllineno, new_string( p ) );
break;
case ERROR:
te = new_Error( tpllineno, new_string( p ) );
break;
}
*link = te;
link = &((*link)->next);
}
}
else {
*link = new_Plain( tpllineno, new_string( inbuf ) );
link = &((*link)->next);
}
}
}
/******************************************************
* *
* VARCHAR expression evaluation *
* *
******************************************************/
/* Given a pointer to an input string 'spi', a pointer to an output string
'spo', and a stop character 'sc', copy and evaluate the string to the
stop character 'sc'. Update the position of '*spi' to point to the stop
character or '\0', and return the evaluated string.
*/
static string alevalto( spi, sc )
char **spi;
register char sc;
{
register string si;
register string cp; /* pointer to constructed string */
unsigned int croom; /* room in constructed string */
register unsigned int six; /* index in constructed string */
char var1[2]; /* buffer for 1 char variable */
char *fnval;
char *v;
string ans;
int len;
si = *spi;
cp = new_string( si );
croom = strlen( si );
six = 0;
if( sevaltr ){
if( sc == '\0' )
fprintf( tracestream, "alevalto: '%s'\n", si );
else
fprintf( tracestream, "alevalto: '%s' to char '%c'\n", si, sc );
}
while( *si != '\0' && *si != sc ){
/* make sure that 1 character will always fit */
if( six>=croom ){
croom += STRSTEP;
cp = ckrealloc( cp, croom+1 );
}
if( *si != VARCHAR ){
cp[six++] = *si++;
continue;
}
si++;
if( *si == ORBRAC ){
si++;
*spi = si;
ans = alevalto( spi, CRBRAC );
si = *spi;
if( *si != CRBRAC ){
(void) sprintf( errarg, "'%c'", CRBRAC );
line_error( NOCLOSEBRAC );
fre_string( ans );
continue;
}
si++;
v = getvar( ans );
fre_string( ans );
if( v == CHARNIL ){
(void) strcpy( errarg, ans );
line_error( VARNOTFOUND );
continue;
}
len = six + strlen( v ) + strlen( si );
if( len > croom ){
croom = len;
cp = ckrealloc( cp, croom+1 );
}
while( *v!='\0' ) cp[six++] = *v++;
continue;
}
if( *si == OCBRAC ){
si++;
*spi = si;
ans = alevalto( spi, CCBRAC );
si = *spi;
if( *si != CCBRAC ){
(void) sprintf( errarg, "'%c'", CCBRAC );
line_error( NOCLOSEBRAC );
fre_string( ans );
continue;
}
si++;
fnval = evalfn( ans );
v = fnval;
fre_string( ans );
len = six + strlen( fnval ) + strlen( si );
if( len > croom ){
croom = len;
cp = ckrealloc( cp, croom+1 );
}
while( *v!='\0' ) cp[six++] = *v++;
fre_string( fnval );
continue;
}
if( *si == OSBRAC ){
si++;
*spi = si;
ans = alevalto( spi, CSBRAC );
si = *spi;
if( *si != CSBRAC ){
(void) sprintf( errarg, "'%c'", CSBRAC );
line_error( NOCLOSEBRAC );
fre_string( ans );
continue;
}
si++;
fnval = evalexpr( ans );
v = fnval;
fre_string( ans );
len = six + strlen( fnval ) + strlen( si );
if( len > croom ){
croom = len;
cp = ckrealloc( cp, croom+1 );
}
while( *v!='\0' ) cp[six++] = *v++;
fre_string( fnval );
continue;
}
if( *si == '\0' ){
cp[six++] = VARCHAR;
continue;
}
if( !isalnum( *si ) ){
cp[six++] = *si++;
continue;
}
var1[0] = *si++;
var1[1] = '\0';
v = getvar( var1 );
if( v == CHARNIL ){
(void) strcpy( errarg, var1 );
line_error( VARNOTFOUND );
continue;
}
len = six + strlen( v ) + strlen( si );
if( len > croom ){
croom = len;
cp = ckrealloc( cp, croom+1 );
}
while( *v!='\0' ) cp[six++] = *v++;
}
cp[six]='\0';
*spi = si;
if( sevaltr ){
fprintf( tracestream, "value is: '%s'\n", cp );
}
return( cp );
}
/******************************************************
* *
* Output file generation *
* *
******************************************************/
/* Copy an ordinary line to the output and replace all VARCHAR
references with the variable.
*/
static void doplain( l, f )
tplelm l;
FILE *f;
{
string is;
string os;
tpllineno = l->Plain.lno;
is = l->Plain.plainline;
os = alevalto( &is, '\0' );
fputs( os, f );
fre_string( os );
putc( '\n', f );
}
/* Handle 'copy' command. */
static void docopy( tpl, outfile )
tplelm tpl;
FILE *outfile;
{
string fname;
FILE *infile;
char buf[CPBUFSIZE];
string is;
string os;
bool busy;
register int n;
tpllineno = tpl->Copy.lno;
is = tpl->Copy.fname;
os = alevalto( &is, '\0' );
scan1par( os, &fname );
fre_string( os );
if( fname == CHARNIL ) return;
infile = ckfopen( fname, "r" );
(void) strcpy( errarg, fname );
fre_string( fname );
busy = TRUE;
do {
n = fread( buf, sizeof( char ), CPBUFSIZE, infile );
if( n<=0 ){
if( ferror( infile ) ){
sys_error( errno );
}
busy = FALSE;
}
else {
n = fwrite( buf, sizeof( char ), n, outfile );
if( n <= 0 && ferror( outfile ) ){
sys_error( errno );
}
}
} while( busy );
fclose( infile );
}
/* Handle 'insert' command. */
static void doinsert( tpl, outfile )
tplelm tpl;
FILE *outfile;
{
string fname;
string oldfname;
FILE *infile;
string is;
string os;
tpllineno = tpl->Insert.lno;
is = tpl->Insert.fname;
os = alevalto( &is, '\0' );
scan1par( os, &fname );
fre_string( os );
if( fname == CHARNIL ) return;
infile = ckfopen( fname, "r" );
oldfname = tplfilename;
tplfilename = fname;
translate( infile, outfile );
fclose( infile );
tplfilename = oldfname;
fre_string( fname );
}
/* Handle 'include' command. */
static void doinclude( tpl, outfile )
tplelm tpl;
FILE *outfile;
{
char *fname;
char *oldfname;
FILE *infile;
char *is;
char *os;
tpllineno = tpl->Include.lno;
is = tpl->Include.fname;
os = alevalto( &is, '\0' );
scan1par( os, &fname );
fre_string( os );
if( fname == CHARNIL ) return;
infile = ckfopen( fname, "r" );
oldfname = tplfilename;
tplfilename = fname;
newvarctx();
setvar( "templatefile", fname );
translate( infile, outfile );
flushvar();
fclose( infile );
tplfilename = oldfname;
fre_string( fname );
}
/* Handle 'error' command. */
static void doerror( tpl )
tplelm tpl;
{
char *is;
char *os;
tpllineno = tpl->Error.lno;
is = tpl->Error.errstr;
os = alevalto( &is, '\0' );
(void) fprintf( stderr, "%s\n", os );
fre_string( os );
}
/* Handle 'exit' command. */
static void doexit( tpl )
tplelm tpl;
{
char *is;
char *os;
tpllineno = tpl->Exit.lno;
is = tpl->Exit.str;
os = alevalto( &is, '\0' );
exit( atoi( os ) );
/* freeing is no use */
}
/* Handle 'set' command. */
static void doset( tpl )
tplelm tpl;
{
char *is;
char *os;
string val;
string_list sl;
string_list nl;
register unsigned int ix;
tpllineno = tpl->Set.lno;
is = tpl->Set.setline;
os = alevalto( &is, '\0' );
sl = chopstring( os );
fre_string( os );
if( sl->sz<1 ){
line_error( NONAME );
rfre_string_list( sl );
return;
}
nl = new_string_list();
for( ix=1; ix<sl->sz; ix++ ){
app_string_list( nl, new_string( sl->arr[ix] ) );
}
val = flatstrings( nl );
rfre_string_list( nl );
setvar( sl->arr[0], val );
rfre_string_list( sl );
fre_string( val );
}
/* Handle 'append' command. */
static void doappend( tpl )
tplelm tpl;
{
char *is;
char *os;
string val;
string_list sl;
string_list nl;
register unsigned int ix;
tpllineno = tpl->Append.lno;
is = tpl->Append.appline;
os = alevalto( &is, '\0' );
sl = chopstring( os );
fre_string( os );
if( sl->sz<1 ){
line_error( NONAME );
rfre_string_list( sl );
return;
}
nl = new_string_list();
val = getvar( sl->arr[0] );
if( val != CHARNIL ){
app_string_list( nl, new_string( val ) );
}
for( ix=1; ix<sl->sz; ix++ ){
app_string_list( nl, new_string( sl->arr[ix] ) );
}
val = flatstrings( nl );
rfre_string_list( nl );
setvar( sl->arr[0], val );
rfre_string_list( sl );
fre_string( val );
}
/* Handle 'if' command. */
static void doif( tpl, outfile )
tplelm tpl;
FILE *outfile;
{
char *is;
char *os;
bool cond;
tpllineno = tpl->If.lno;
is = tpl->If.ifcond;
os = alevalto( &is, '\0' );
cond = istruestr( os );
fre_string( os );
if( cond ){
dotrans( tpl->If.ifthen, outfile );
}
else {
dotrans( tpl->If.ifelse, outfile );
}
}
/* Handle 'foreach' command.
Given a list of template lines, starting with a foreach command line,
generate output lines. Handle local commands by recursion.
*/
static void doforeach( tpl, outfile )
tplelm tpl;
FILE *outfile;
{
char *nm;
char *is;
char *os;
unsigned int ix;
string_list sl;
tpllineno = tpl->Foreach.lno;
is = tpl->Foreach.felist;
os = alevalto( &is, '\0' );
sl = chopstring( os );
fre_string( os );
if( sl->sz<1 ){
line_error( NONAME );
rfre_string_list( sl );
return;
}
nm = sl->arr[0];
for( ix=1; ix<sl->sz; ix++ ){
setvar( nm, sl->arr[ix] );
dotrans( tpl->Foreach.felines, outfile );
}
rfre_string_list( sl );
}
/* Handle 'while' command.
Given a list of template lines, starting with a while command line,
generate output lines until condition is false. Handle local commands
by recursion.
*/
static void dowhile( tpl, outfile )
tplelm tpl;
FILE *outfile;
{
bool done;
char *is;
char *os;
tpllineno = tpl->While.lno;
while( TRUE ){
is = tpl->While.whilecond;
os = alevalto( &is, '\0' );
done = isfalsestr( os );
fre_string( os );
if( done ) return;
dotrans( tpl->While.whilelines, outfile );
}
}
/* Recursive translation routine.
Given a list of template lines in 'tpl' generate text from them,
and write this text to 'outfile'.
*/
static void dotrans( tpl, outfile )
tplelm tpl;
FILE *outfile;
{
while( tpl != tplelmNIL ){
switch( tpl->tag ){
case TAGError:
doerror( tpl );
break;
case TAGForeach:
doforeach( tpl, outfile );
break;
case TAGIf:
doif( tpl, outfile );
break;
case TAGPlain:
doplain( tpl, outfile );
break;
case TAGSet:
doset( tpl );
break;
case TAGAppend:
doappend( tpl );
break;
case TAGInclude:
doinclude( tpl, outfile );
break;
case TAGInsert:
doinsert( tpl, outfile );
break;
case TAGCopy:
docopy( tpl, outfile );
break;
case TAGExit:
doexit( tpl );
break;
case TAGWhile:
dowhile( tpl, outfile );
break;
default:
(void) sprintf( errarg, "%d", tpl->tag );
crash( BADTAG );
}
tpl = tpl->next;
}
}
/******************************************************
* *
* Top level of translation routines *
* *
******************************************************/
void translate( infile, outfile )
FILE *infile;
FILE *outfile;
{
tplelm template;
int endcom;
tpllineno = 0;
template = readtemplate( infile, &endcom );
if( endcom != EOFLINE ){
unbalance( tpllineno, endcom, EOFLINE );
return;
}
dotrans( template, outfile );
rfre_tplelm_list( template );
}